/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */

import type { TreeBranch } from "../simple-tree/index.js";

/**
 * Allows reversion of a change made to SharedTree.
 *
 * @remarks
 * Applications wanting to implement undo/redo support might typically maintain two stacks of Revertibles, with optional eviction policy to free up memory.
 *
 * @sealed @public
 */
export interface Revertible {
	/**
	 * The current status of the revertible.
	 */
	readonly status: RevertibleStatus;

	/**
	 * Reverts the associated change and disposes it.
	 */
	revert(): void;
	/**
	 * Reverts the associated change and optionally disposes it.
	 *
	 * @param dispose - If true, the revertible will be disposed after being reverted.
	 * If false, the revertible will remain valid. This can be useful for scenarios where the revert may be dropped
	 * due to merge conflicts, and one wants to attempt reverting again.
	 */
	revert(dispose: boolean): void;

	/**
	 * Disposes this revertible, allowing associated resources to be released.
	 */
	dispose(): void;
}

/**
 * A {@link Revertible} with features that are not yet stable.
 *
 * @sealed @alpha
 */
export interface RevertibleAlpha extends Revertible {
	/**
	 * Clones the {@link Revertible} to a target branch.
	 *
	 * @param branch - A target branch to apply the revertible to.
	 * The target branch must contain the same commit that this revertible is meant to revert, otherwise will throw an error.
	 * @returns A cloned revertible is independent of the original, meaning disposing of one will not affect the other,
	 * provided they do not belong to the same branch. Both revertibles can be reverted independently.
	 */
	clone: (branch: TreeBranch) => RevertibleAlpha;
}

/**
 * The status of a {@link Revertible}.
 *
 * @public
 */
export enum RevertibleStatus {
	/** The revertible can be reverted. */
	Valid,
	/** The revertible has been disposed. Reverting it will have no effect. */
	Disposed,
}

/**
 * Factory for creating a {@link Revertible}.
 * @throws error if invoked outside the scope of the `changed` event that provides it, or if invoked multiple times.
 *
 * @param onRevertibleDisposed - A callback that will be invoked when the `Revertible` generated by this factory is disposed.
 * This happens when the `Revertible` is disposed manually, or when the `TreeView` that the `Revertible` belongs to is disposed,
 * whichever happens first.
 * This is typically used to clean up any resources associated with the `Revertible` in the host application.
 *
 * @sealed @public
 */
export type RevertibleFactory = (
	onRevertibleDisposed?: (revertible: Revertible) => void,
) => Revertible;

/**
 * Factory for creating a {@link RevertibleAlpha}.
 * @throws error if invoked outside the scope of the `changed` event that provides it, or if invoked multiple times.
 *
 * @param onRevertibleDisposed - A callback that will be invoked when the {@link RevertibleAlpha | Revertible} generated by this factory is disposed.
 * This happens when the `Revertible` is disposed manually, or when the `TreeView` that the `Revertible` belongs to is disposed,
 * whichever happens first.
 * This is typically used to clean up any resources associated with the `Revertible` in the host application.
 *
 * @sealed @alpha
 */
export type RevertibleAlphaFactory = (
	onRevertibleDisposed?: (revertible: RevertibleAlpha) => void,
) => RevertibleAlpha;
